#!/usr/sbin/nft -f

{% import 'firewall/nftables-defines.j2' as group_tmpl %}
{% import 'firewall/nftables-bridge.j2' as bridge_tmpl %}
{% import 'firewall/nftables-offload.j2' as offload_tmpl %}
{% import 'firewall/nftables-zone.j2' as zone_tmpl %}

flush chain raw vyos_global_rpfilter
flush chain ip6 raw vyos_global_rpfilter

table raw {
    chain vyos_global_rpfilter {
{% if global_options.source_validation is vyos_defined('loose') %}
        fib saddr oif 0 counter drop
{% elif global_options.source_validation is vyos_defined('strict') %}
        fib saddr . iif oif 0 counter drop
{% endif %}
        return
    }
}

table ip6 raw {
    chain vyos_global_rpfilter {
{% if global_options.ipv6_source_validation is vyos_defined('loose') %}
        fib saddr oif 0 counter drop
{% elif global_options.ipv6_source_validation is vyos_defined('strict') %}
        fib saddr . iif oif 0 counter drop
{% endif %}
        return
    }
}

{% if first_install is not vyos_defined %}
delete table ip vyos_filter
{% endif %}
table ip vyos_filter {
{% if ipv4 is vyos_defined %}
{%     if flowtable is vyos_defined %}
{%         for name, flowtable_conf in flowtable.items() %}
{{ offload_tmpl.flowtable(name, flowtable_conf) }}
{%         endfor %}
{%     endif %}

{%     set ns = namespace(sets=[]) %}
{%     if ipv4.forward is vyos_defined %}
{%         for prior, conf in ipv4.forward.items() %}
    chain VYOS_FORWARD_{{ prior }} {
        type filter hook forward priority {{ prior }}; policy accept;
{%             if global_options.state_policy is vyos_defined %}
        jump VYOS_STATE_POLICY
{%             endif %}
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('FWD', prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('FWD-' + prior, 'ipv4') }}
    }
{%         endfor %}
{%     endif %}

{%     if ipv4.input is vyos_defined %}
{%         for prior, conf in ipv4.input.items() %}
    chain VYOS_INPUT_{{ prior }} {
        type filter hook input priority {{ prior }}; policy accept;
{%             if global_options.state_policy is vyos_defined %}
        jump VYOS_STATE_POLICY
{%             endif %}
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('INP',prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('INP-' + prior, 'ipv4') }}
    }
{%         endfor %}
{%     endif %}

{%     if ipv4.output is vyos_defined %}
{%         for prior, conf in ipv4.output.items() %}
    chain VYOS_OUTPUT_{{ prior }} {
        type filter hook output priority {{ prior }}; policy accept;
{%             if global_options.state_policy is vyos_defined %}
        jump VYOS_STATE_POLICY
{%             endif %}
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('OUT', prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['OUT_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('OUT-' + prior, 'ipv4') }}
    }
{%         endfor %}
{%     endif %}

{%     if ipv4.prerouting is vyos_defined %}
{%         for prior, conf in ipv4.prerouting.items() %}
    chain VYOS_PREROUTING_{{ prior }} {
        type filter hook prerouting priority {{ prior }}; policy accept;
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('PRE', prior, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['PRE_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('PRE-' + prior, 'ipv4') }}
    }
{%         endfor %}
{%     endif %}

    chain VYOS_FRAG_MARK {
        type filter hook prerouting priority -450; policy accept;
        ip frag-off & 0x3fff != 0 meta mark set 0xffff1 return
    }

{%     if ipv4.name is vyos_defined %}
{%         for name_text, conf in ipv4.name.items() %}
    chain NAME_{{ name_text }} {
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('NAM', name_text, rule_id) }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule(name_text, 'ipv4') }}
    }
{%         endfor %}
{%     endif %}

{%     for set_name in ns.sets %}
    set RECENT_{{ set_name }} {
        type ipv4_addr
        size 65535
        flags dynamic
    }
{%     endfor %}
{%     for set_name in ip_fqdn %}
    set FQDN_{{ set_name }} {
        type ipv4_addr
        flags interval
    }
{%     endfor %}
{%     if geoip_updated.name is vyos_defined %}
{%         for setname in geoip_updated.name %}
    set {{ setname }} {
        type ipv4_addr
        flags interval
    }
{%         endfor %}
{%     endif %}
{% endif %}
{{ group_tmpl.groups(group, False, True) }}

{% if zone is vyos_defined %}
{{ zone_tmpl.zone_chains(zone, False, global_options.state_policy is vyos_defined) }}
{% endif %}
{% if global_options.state_policy is vyos_defined %}
    chain VYOS_STATE_POLICY {
{%     if global_options.state_policy.established is vyos_defined %}
        {{ global_options.state_policy.established | nft_state_policy('established') }}
{%     endif %}
{%     if global_options.state_policy.invalid is vyos_defined %}
        {{ global_options.state_policy.invalid | nft_state_policy('invalid') }}
{%     endif %}
{%     if global_options.state_policy.related is vyos_defined %}
        {{ global_options.state_policy.related | nft_state_policy('related') }}
{%     endif %}
        return
    }
{% endif %}
}

{% if first_install is not vyos_defined %}
delete table ip6 vyos_filter
{% endif %}
table ip6 vyos_filter {
{% if ipv6 is vyos_defined %}
{%     if flowtable is vyos_defined %}
{%         for name, flowtable_conf in flowtable.items() %}
{{ offload_tmpl.flowtable(name, flowtable_conf) }}
{%         endfor %}
{%     endif %}

{%     set ns = namespace(sets=[]) %}
{%     if ipv6.forward is vyos_defined %}
{%         for prior, conf in ipv6.forward.items() %}
    chain VYOS_IPV6_FORWARD_{{ prior }} {
        type filter hook forward priority {{ prior }}; policy accept;
{%             if global_options.state_policy is vyos_defined %}
        jump VYOS_STATE_POLICY6
{%             endif %}
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('FWD', prior, rule_id, 'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['FWD_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('FWD-' + prior, 'ipv6') }}
    }
{%         endfor %}
{%     endif %}

{%     if ipv6.input is vyos_defined %}
{%         for prior, conf in ipv6.input.items() %}
    chain VYOS_IPV6_INPUT_{{ prior }} {
        type filter hook input priority {{ prior }}; policy accept;
{%             if global_options.state_policy is vyos_defined %}
        jump VYOS_STATE_POLICY6
{%             endif %}
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('INP', prior, rule_id, 'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['INP_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('INP-' + prior, 'ipv6') }}
    }
{%         endfor %}
{%     endif %}

{%     if ipv6.output is vyos_defined %}
{%         for prior, conf in ipv6.output.items() %}
    chain VYOS_IPV6_OUTPUT_{{ prior }} {
        type filter hook output priority {{ prior }}; policy accept;
{%             if global_options.state_policy is vyos_defined %}
        jump VYOS_STATE_POLICY6
{%             endif %}
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('OUT', prior, rule_id, 'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['OUT_ ' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('OUT-' + prior, 'ipv6') }}
    }
{%         endfor %}
{%     endif %}

{%     if ipv6.prerouting is vyos_defined %}
{%         for prior, conf in ipv6.prerouting.items() %}
    chain VYOS_IPV6_PREROUTING_{{ prior }} {
        type filter hook prerouting priority {{ prior }}; policy accept;
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('PRE', prior, rule_id, 'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['PRE_' + prior + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule('PRE-' + prior, 'ipv6') }}
    }
{%         endfor %}
{%     endif %}
    chain VYOS_FRAG6_MARK {
        type filter hook prerouting priority -450; policy accept;
        exthdr frag exists meta mark set 0xffff1 return
    }

{%     if ipv6.name is vyos_defined %}
{%         for name_text, conf in ipv6.name.items() %}
    chain NAME6_{{ name_text }} {
{%             if conf.rule is vyos_defined %}
{%                 for rule_id, rule_conf in conf.rule.items() if rule_conf.disable is not vyos_defined %}
        {{ rule_conf | nft_rule('NAM', name_text, rule_id, 'ip6') }}
{%                     if rule_conf.recent is vyos_defined %}
{%                         set ns.sets = ns.sets + ['NAM_' + name_text + '_' + rule_id] %}
{%                     endif %}
{%                 endfor %}
{%             endif %}
        {{ conf | nft_default_rule(name_text, 'ipv6') }}
    }
{%         endfor %}
{%     endif %}

{%     for set_name in ns.sets %}
    set RECENT6_{{ set_name }} {
        type ipv6_addr
        size 65535
        flags dynamic
    }
{%     endfor %}
{%     for set_name in ip6_fqdn %}
    set FQDN_{{ set_name }} {
        type ipv6_addr
        flags interval
    }
{%     endfor %}
{%     if geoip_updated.ipv6_name is vyos_defined %}
{%         for setname in geoip_updated.ipv6_name %}
    set {{ setname }} {
        type ipv6_addr
        flags interval
    }
{%         endfor %}
{%     endif %}
{% endif %}
{{ group_tmpl.groups(group, True, True) }}
{% if zone is vyos_defined %}
{{ zone_tmpl.zone_chains(zone, True, global_options.state_policy is vyos_defined) }}
{% endif %}
{% if global_options.state_policy is vyos_defined %}
    chain VYOS_STATE_POLICY6 {
{%     if global_options.state_policy.established is vyos_defined %}
        {{ global_options.state_policy.established | nft_state_policy('established') }}
{%     endif %}
{%     if global_options.state_policy.invalid is vyos_defined %}
        {{ global_options.state_policy.invalid | nft_state_policy('invalid') }}
{%     endif %}
{%     if global_options.state_policy.related is vyos_defined %}
        {{ global_options.state_policy.related | nft_state_policy('related') }}
{%     endif %}
        return
    }
{% endif %}
}

## Bridge Firewall
{% if first_install is not vyos_defined %}
delete table bridge vyos_filter
{% endif %}
table bridge vyos_filter {
{{ bridge_tmpl.bridge(bridge) }}
{{ group_tmpl.groups(group, False, False) }}

}
